package com.ckzp.core.lc;


import com.jfinal.kit.Base64Kit;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * RSA非对称加解密
 * Created by 51594 on 2023/4/23.
 */
public class Rsa {

    private final static String ALGORITHM = "RSA";

    /**
     * 直接生成公钥、私钥对象
     */
    public static KeyPair getKeyPair() {
        return getKeyPair(1024);
    }

    public static KeyPair getKeyPair(int keySize) {
        try {
            KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM);
            generator.initialize(keySize);
            return generator.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 公钥加密
     */
    public static String encrypt(RSAPublicKey key, String data) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            int maxSingleSize = key.getModulus().bitLength() / 8 - 11;
            byte[][] dataArray = splitArray(data.getBytes(), maxSingleSize);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            for (byte[] s : dataArray) {
                out.write(cipher.doFinal(s));
            }
            return Base64Kit.encode(out.toByteArray());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static String encrypt(KeyPair key, String data) {
        return encrypt((RSAPublicKey) key.getPublic(), data);
    }

    public static String encrypt(String key, String data) {
        return encrypt((RSAPublicKey) toKey(key, true), data);
    }

    /**
     * 私钥解密
     */
    public static String decrypt(RSAPrivateKey keyPair, String data) {
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, keyPair);
            int modulusSize = keyPair.getModulus().bitLength() / 8;
            byte[] decodeData = Base64Kit.decode(data);
            byte[][] splitArrays = splitArray(decodeData, modulusSize);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            for (byte[] arr : splitArrays) {
                out.write(cipher.doFinal(arr));
            }
            return new String(out.toByteArray());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static String decrypt(KeyPair key, String data) {
        return decrypt((RSAPrivateKey) key.getPrivate(), data);
    }

    public static String decrypt(String key, String data) {
        return decrypt((RSAPrivateKey) toKey(key, false), data);
    }


    /**
     * 钥对象 转换为 字符串 方便数据库存储
     */
    public static String toStr(Key key) {
        return Base64Kit.encode(key.getEncoded());
    }

    public static String toStrByPublic(KeyPair key) {
        return toStr(key.getPublic());
    }

    public static String toStrByPrivate(KeyPair key) {
        return toStr(key.getPrivate());
    }

    /**
     * 字符串钥 转换为 钥对象 方便使用
     */
    public static <T> T toKey(String key, boolean isPublic) {
        try {
            KeyFactory factory = KeyFactory.getInstance(ALGORITHM);
            byte[] bytes = Base64Kit.decode(key);
            if (isPublic) {
                return (T) factory.generatePublic(new X509EncodedKeySpec(bytes));
            }
            return (T) factory.generatePrivate(new PKCS8EncodedKeySpec(bytes));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static byte[][] splitArray(byte[] data, int len) {
        int dataLen = data.length;
        if (dataLen <= len) {
            return new byte[][]{data};
        }
        byte[][] result = new byte[(dataLen - 1) / len + 1][];
        int resultLen = result.length;
        for (int i = 0; i < resultLen; i++) {
            if (i == resultLen - 1) {
                int length = dataLen - len * i;
                byte[] single = new byte[length];
                System.arraycopy(data, len * i, single, 0, length);
                result[i] = single;
                break;
            }
            byte[] single = new byte[len];
            System.arraycopy(data, len * i, single, 0, len);
            result[i] = single;
        }
        return result;
    }


    public static void main(String[] arg) {
        KeyPair key = Rsa.getKeyPair();
        String s = "Abc 中文 @#";
//        System.out.println(s);

        //加密
        String str = Rsa.encrypt(key, s);
        //解密
//        String s1 = Rsa.decrypt(key, str);
//        System.out.println(str);
//        System.out.println(s1);
//        System.out.println("-----------");

        //公钥字符串
        String publicStr = Rsa.toStrByPublic(key);
        //私钥字符串
        String privateStr = Rsa.toStrByPrivate(key);
//        System.out.println("------------------------public key-------------------------------------");
//        System.out.println(publicStr);
//        System.out.println("------------------------private key-------------------------------------");
//
//        System.out.println(privateStr);
//        System.out.println("-----------");

        //加密
        str = Rsa.encrypt(publicStr, s);
        //解密
        //s1 = Rsa.decrypt(privateStr, str);
//        System.out.println(str);
//        System.out.println(s1);
//        System.out.println("-----------");
    }
}